package edu.ncsu.csc.ip.client;

import java.io.File;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.Timer;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CopyOnWriteArrayList;

import edu.ncsu.csc.ip.client.timer.TTLUpdateTask;
import edu.ncsu.csc.ip.common.Log;
import edu.ncsu.csc.ip.server.ClientBean;
import edu.ncsu.csc.ip.server.ClientIndex;

/**
 * Contains the RFCIndex object and all of it's functions.
 * The index is a Map between an integer and the RFCBean.
 * 
 * 
 * 
 * @author Moin Ayazifar <mayazif@ncsu.edu>
 * @author Steven Elliott <sdelliot@ncsu.edu>
 * 
 */

public class RFCIndex {

	public final static Integer TTL_UPDATE_INTERVAL = 60;

	// private final String rfcBundleFolder = "./rfd/";
	private final String rfcFileExtension = ".txt";
	private final String rfcFilePreName = "rfc";

	private ClientInfo clientInfo;

	private Map<Integer, RFCBean> localRfcIndexMap = new ConcurrentHashMap<Integer, RFCBean>();

	private Map<Integer, List<RFCBean>> rfcIndexMap = new ConcurrentHashMap<Integer, List<RFCBean>>();

	public RFCIndex(ClientInfo clientInfo) {
		this.clientInfo = clientInfo;

		File folder = new File(this.clientInfo.getRfcBundleFolder());
		// create folder if doesn't exist
		if (!folder.isDirectory())
			folder.mkdir();
		File[] listOfFiles = folder.listFiles();

		for (File file : listOfFiles) {
			if (file.isFile() && file.getName().startsWith(rfcFilePreName)
					&& file.getName().indexOf(rfcFileExtension) > -1) {
				RFCBean rfcBean = new RFCBean();
				try {
					int rfcNumber = Integer.parseInt(file.getName().substring(
							rfcFilePreName.length(),
							file.getName().indexOf(rfcFileExtension)));
					rfcBean.setRfcNumber(rfcNumber);
					rfcBean.setRfcTitle(rfcFilePreName + " " + rfcNumber);
					rfcBean.setAddress(this.clientInfo.getLocalAddress());
					rfcBean.setHostname(this.clientInfo.getLocalHostName());
					rfcBean.setPort(this.clientInfo.getRfcServerPort());

					getLocalRfcIndexMap().put(rfcNumber, rfcBean);
					
					Log.println(clientInfo + this.clientInfo.getRfcBundleFolder()
							+ file.getName()+ " added to local RFC Index");
				} catch (NumberFormatException nfe) {
					Log.println(clientInfo +file.getName()
							+ " is not a valid rfc file name. "
							+ nfe.getMessage());
				}

			}
		}

	}

	public String getRFCFileName(Integer rfcNumber) {
		return getRFCFileName(String.valueOf(rfcNumber));
	}

	public String getRFCFileName(String rfcNumber) {
		return this.clientInfo.getRfcBundleFolder() + "rfc" + rfcNumber
				+ ".txt";
	}

	public String getLocalRFCFileName(Integer rfcNumber) {
		if (getLocalRfcIndexMap().containsKey(rfcNumber))
			return getRFCFileName(rfcNumber);
		else
			return null;
	}

	/**
	 * @return the localRfcIndexMap
	 */
	public Map<Integer, RFCBean> getLocalRfcIndexMap() {
		return localRfcIndexMap;
	}

	/**
	 * @return the rfcIndexMap
	 */
	public Map<Integer, List<RFCBean>> getRfcIndexMap() {
		return rfcIndexMap;
	}

	public void addLocalRFC(RFCBean rfcBean) {
		rfcBean.setAddress(this.clientInfo.getLocalAddress());
		rfcBean.setHostname(this.clientInfo.getLocalHostName());
		rfcBean.setPort(this.clientInfo.getRfcServerPort());
		localRfcIndexMap.put(rfcBean.getRfcNumber(), rfcBean);
	}

	public void addGlobalRFC(RFCBean rfcBean) {
		if (!(localRfcIndexMap.containsKey(rfcBean.getRfcNumber()) && localRfcIndexMap
				.get(rfcBean.getRfcNumber()).equals(rfcBean))) {
			if (rfcIndexMap.containsKey(rfcBean.getRfcNumber())) {
				rfcIndexMap.get(rfcBean.getRfcNumber()).add(rfcBean);
			} else {
				List<RFCBean> rfcList = new CopyOnWriteArrayList<RFCBean>();
				rfcList.add(rfcBean);
				rfcIndexMap.put(rfcBean.getRfcNumber(), rfcList);
			}
		}
	}

	public void addAllGlobalRFC(List<RFCBean> rfcBeanList) {
		for (RFCBean rfcBean : rfcBeanList)
			addGlobalRFC(rfcBean);
	}

	public List<RFCBean> mergeGlobalLocalRFC() {

		Set<RFCBean> rfcSet = new HashSet<RFCBean>();
		for (List<RFCBean> rfcList : rfcIndexMap.values())
			for (RFCBean rfcBean : rfcList)
				rfcSet.add(rfcBean);

		for (RFCBean rfcBean : localRfcIndexMap.values())
			rfcSet.add(rfcBean);
		List<RFCBean> list = new ArrayList<RFCBean>();
		list.addAll(rfcSet);
		return list;

	}

	/*
	 * Remove Inactive RFC beans after getting a fresh list of active peers
	 */
	public void removeInactiveRFCBeans(List<ClientBean> clientBeanList) {

		if (clientBeanList!=null && clientBeanList.size() > 0) {
			Set<String> clientAddressMap = new HashSet<String>();
			for (ClientBean clientBean : clientBeanList) {
				clientAddressMap.add(clientBean.getIpAddress()
						+ clientBean.getRfcServerPort());
			}

			for (List<RFCBean> rfcList : rfcIndexMap.values()) {
				for (RFCBean rfcBean : rfcList) {
					if (!clientAddressMap.contains(rfcBean.getAddress()
							+ rfcBean.getPort())) {
						rfcList.remove(rfcBean);
					}
				}
			}

		}else{ //No active clients so clear the RFC Index
			rfcIndexMap.clear();
		}
	}

}
